package com.pic.mgt.haishin.executer;

import java.io.File;

import org.springframework.dao.DataAccessException;

import com.pic.mgt.IgxStatus;
import com.pic.mgt.dao.haishin.IHaishinBusinessMgtDao;
import com.pic.mgt.dao.haishin.IHaishinBusinessTargetActionDao;
import com.pic.mgt.executer.TargetAction;
import com.pic.mgt.executer.TargetFileProcessException;
import com.pic.mgt.haishin.executer.tar.DeTarFile;
import com.pic.mgt.haishin.executer.tar.DeTarFileChecker;
import com.pic.mgt.haishin.executer.tar.DeTarFileException;
import com.pic.mgt.haishin.executer.tar.EntryListener;
import com.pic.mgt.haishin.executer.tar.DeTarFile.DeTarResult;
import com.pic.mgt.haishin.unit.HaishinBusinessUnit;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.unit.Business;
import com.pic.mgt.unit.Redoable;

/**
 * [abstract class] 處理目標檔的解Tar
 * @author 洪政緯
 *
 */
public abstract class HaishinTargetFileProcess 
							implements TargetAction,
								       DeTarFileChecker,
									   EntryListener {
	
	public static enum TerminalProcessResult {
		TerminalProcessComplete,
		IneffectiveTerminal}
	
	private DeTarFile deTarFile;
	private TerminalListener terminalListener;
	private boolean targetFileProcessing = false;
	protected String haishinOutboxPahtStr;
	private int terminaIdStartFromChar;
	private int terminalIdEndToChar;
	private int deTarFilenameLen;
	protected IHaishinBusinessMgtDao haishinBusinessMgtDao; 
	protected IHaishinBusinessTargetActionDao haishinBusinessTargetActionDao;
	
	protected HaishinBusinessUnit haishinBusiness;
	private boolean isRedoMode;
	
	HaishinTargetFileProcess(DeTarFile deTarFile) {
		this.deTarFile = deTarFile;
		
		this.deTarFile.setDeTarFileChecker(this);
		this.deTarFile.setGZipDecompress(false);
	}
	
	// 實作 TargetAction Interface
	@Override
	public synchronized void perform(Business business)
								throws TargetFileProcessException {
		this.haishinBusiness = (HaishinBusinessUnit)business;
		
		this.isRedoMode = Redoable.class.isInstance(business);
		
		// 如果是 redo 的業務，則讓子類做前置處理
		if (this.isRedoMode) {
			doRedoHaishinBusinessPreprocess();
		}
		
		targetFileProcessing = true;
		File targetFile = business.getTargetFile();
		try {
			// ********************** Log **********************
			if (IgxLog.messageDetailsLog.isInfoMode()) {
				IgxLog.messageDetailsLog.info(this, "De-tar target file '{}' to terminals.",
						targetFile.getName());
			}
			// ********************** Log **********************
			
			deTarFile.deTarFile(targetFile);
			
			// 更新資料庫狀態
			// throws DataAccessException
			updateHaishinBusinessMgtDbStatusToFinish();
			
			// ********************** Log **********************
			if (IgxLog.messageDetailsLog.isInfoMode()) {
				IgxLog.messageDetailsLog.info(this, "De-tar target file '{}' to terminals completed.",
						targetFile.getName());
			}
			// ********************** Log **********************
		} catch(DataAccessException e1) {
			throw new TargetFileProcessException(
					"Target File '" + targetFile + "' process failed in final updating database status (table: "
					+ this.haishinBusinessMgtDao.getTableName() + ").",
					e1);
		} catch (DeTarFileException e2) {	
			throw new TargetFileProcessException(
					"Target File '" + targetFile + "' process failed.",
					e2);
		} finally {
			targetFileProcessing = false;
			
			deTarFinish();
		}
	}
	
	/**
	 * 當目標檔案解 tar 完成後,會更新資料庫 (haishin business mgt table)的狀態為執行完成
	 * (BUSINESS_EXECUTING_FINISH)
	 * @param status
	 * @throws DataAccessException
	 */
	protected void updateHaishinBusinessMgtDbStatusToFinish() throws DataAccessException {
		this.haishinBusinessMgtDao.updateStatusByUnitName(IgxStatus.BUSINESS_EXECUTING_FINISH,
														  this.haishinBusiness.getUnitName());
	}
	
	/**
	 * 當對配信目標檔案 處理過程中，可以中斷其處理。
	 */
	private void interruptPerform() {
		if (null != this.deTarFile) {
			this.deTarFile.interruptDeTarAction();
		}
	}
	
	@Override
	public File checkDeTarFile(String toBeDetaredFileName)
								throws InvalidTerminalException {
		
		// 先驗證要解tar的檔案長度是否正確
		if (toBeDetaredFileName.length() != this.deTarFilenameLen) {
			//if (toBeDetaredFileName.indexOf("_IRDD_") < 0 ||
				//toBeDetaredFileName.length() != (this.deTarFilenameLen+11)) { // 刪除此 if 判斷 (區塊內的內容要留著)
				// 長度格式不正確，不能解該檔，寫錯誤 Log 並回傳null
				
				// ********************** Log **********************
				IgxLog.messageLog.error(this,
						"SUDA-228 The processing file '{}' contained in target file '{}' has an wrong filename length.",
						toBeDetaredFileName, haishinBusiness.getTargetFilename());
				// ********************** Log **********************
				
				// (回傳 null 表示忽略此檔，不要進行任何動作。)
				return null;
			//}
		}
		
		
		// 從檔名中取出端末 ID
		String terminalIdStrInToBeDetaredFileName =
							getTerminalIdFromFileName(toBeDetaredFileName);
		
		/*
		 *  如果現在正在進行的業務是 redo 的,則呼叫要讓子類去檢查,
		 *  在 redo 模式下,該交易檔是否要被處理.如果沒有需要處理則忽略
		 */
		if (this.isRedoMode) {
			boolean isFilteredInRedoMode = isFilteredTxFileInRedoMode(
												terminalIdStrInToBeDetaredFileName,
												toBeDetaredFileName);
			if (isFilteredInRedoMode) {
				return null;
			}
		}
		
		// 交由子類別確認端末ID是否正確或有效
		boolean isEffectiveTerminal = 
			validateTargetDest(terminalIdStrInToBeDetaredFileName);
		if (isEffectiveTerminal) {
			return
					new File(this.haishinOutboxPahtStr,
							 terminalIdStrInToBeDetaredFileName);
		}
		else {
			throw new InvalidTerminalException(
					"The processing file '" + toBeDetaredFileName
					+ "' contained in target file '" + haishinBusiness.getTargetFilename() +
					"' presented a invalid terminal '" + terminalIdStrInToBeDetaredFileName + "'.");
		}
	}
	
	/**
	 * 實作 EntryListener Interface 方法。
	 * 當在進行 Target File 解 Tar 動作過程中，每解完一筆檔案或目錄
	 * ，就會觸發此方式通知。 
	 */
	@Override
	public void entryDeTarNotify(File completedEntry,
								 String entryName,
								 DeTarResult deTarResult) {

		String terminal = getTerminalIdFromFileName(completedEntry.getName());
		
		TerminalProcessResult terminalProcessResult = null;
		switch(deTarResult) {
		case EntryIsCompleted :
			terminalProcessResult = TerminalProcessResult.TerminalProcessComplete;
			break;
		case EntryBeFiltered :
			terminalProcessResult = TerminalProcessResult.IneffectiveTerminal;
			break;
		}
		
		boolean isContinue = 
			this.terminalListener.terminalProcessNotify(haishinBusiness,
														terminal,
														completedEntry,
														terminalProcessResult);
		if (!isContinue) {
			interruptPerform();
		}
	}
	
	private String getTerminalIdFromFileName(String filename) {
		return 
			filename.substring(this.terminaIdStartFromChar - 1,
							   this.terminalIdEndToChar);
	}
	
	/**
	 * 檢查是否為有效端末，若否拋出例外。
	 * @param firstLayerFolderName
	 * @throws InvalidTerminalException
	 */
	protected abstract boolean validateTargetDest(String firstLayerFolderName);
	
	/**
	 * 如果是 redo 的業務,則該方法會被呼叫，用來做重覆業務的前置處理。
	 */
	protected abstract void doRedoHaishinBusinessPreprocess() throws TargetFileProcessException;
	
	/**
	 * 若是 redo 的業務,則在做每一個端末檢查時,會先呼叫此方法. 用來確認在
	 * redo 模式下,該交易檔是否要要被處理.
	 * @param deTarterminal
	 * @param deTarFilename
	 * @return
	 */
	protected abstract boolean isFilteredTxFileInRedoMode(String deTarterminal,
														    String deTarFilename);
	
	/**
	 * 當解 tar 的動作完成時,此方法會被呼叫.
	 */
	protected abstract void deTarFinish();
	
	public void setTerminalListener(
				TerminalListener terminalDeTarCompleteNotify) {
		if (!this.targetFileProcessing) {
			this.terminalListener = terminalDeTarCompleteNotify;
			this.deTarFile.setEntryListener(this);
		}
	}
	
	void removeTerminalListener() {
		if (!this.targetFileProcessing) {
			this.terminalListener = null;
			this.deTarFile.removeEntryListener();
		}
	}
	
	TerminalListener getTerminalListener() {
		return this.terminalListener;
	}

	// --- getter and setter ---
	
	public void setTaishinOutboxPahtStr(String haishinOutboxPahtStr) {
		this.haishinOutboxPahtStr = haishinOutboxPahtStr;
	}

	public void setTerminaIdStartFromChar(int terminaIdStartFromChar) {
		this.terminaIdStartFromChar = terminaIdStartFromChar;
	}

	public void setTerminalIdEndToChar(int terminalIdEndToChar) {
		this.terminalIdEndToChar = terminalIdEndToChar;
	}

	public void setDeTarFilenameLen(int deTarFilenameLen) {
		this.deTarFilenameLen = deTarFilenameLen;
	}

	public void setHaishinBusinessMgtDao(
			IHaishinBusinessMgtDao haishinBusinessMgtDao) {
		this.haishinBusinessMgtDao = haishinBusinessMgtDao;
	}

	public void setHaishinBusinessTargetActionDao(
			IHaishinBusinessTargetActionDao haishinBusinessTargetActionDao) {
		this.haishinBusinessTargetActionDao = haishinBusinessTargetActionDao;
	}
}
